﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace GalaxyDefenderChaos
{
    public class PlanarButton : VisibleGameEntity
    {
        PlanarModel _PlanarModel;

        public PlanarModel PlanarModel
        {
            get { return _PlanarModel; }
            set { _PlanarModel = value; }
        }

        GraphicsDevice _GraphicsDevice;

        public PlanarButton(ContentManager content, String textureStr, Vector2 size, Vector3 position, Vector3 normal, Vector3 up, GraphicsDevice graphicsDevice)
        {
            _PlanarModel = new PlanarModel(content, textureStr, size, position, normal, up, graphicsDevice);
            _GraphicsDevice = graphicsDevice;
        }

        public override void Draw(GraphicsDevice graphicsDevice, Camera camera, GameTime gameTime)
        {
            _PlanarModel.Draw(graphicsDevice, camera, gameTime);
        }

        public bool CheckInside(Vector2 point, Camera camera)
        {
            Ray ray = CreateRay(point, camera);
            Vector3 upperLeft = _PlanarModel.Vertices[1].Position;
            Vector3 upperRight = _PlanarModel.Vertices[3].Position;
            Vector3 lowerLeft = _PlanarModel.Vertices[0].Position;
            Vector3 lowerRight = _PlanarModel.Vertices[2].Position;
            float? checkTriangle1;
            float? checkTriangle2;
            RayIntersectsTriangle(ref ray, ref upperLeft, ref upperRight, ref lowerRight, out checkTriangle1);
            RayIntersectsTriangle(ref ray, ref upperLeft, ref lowerRight, ref lowerLeft, out checkTriangle2);
            if (checkTriangle1 != null || checkTriangle2 != null)
                return true;
            return false;
        }

        private Ray CreateRay(Vector2 point, Camera camera)
        {
            // create 2 positions in screenspace using the cursor position. 0 is as
            // close as possible to the camera, 1 is as far away as possible.
            Vector3 nearSource = new Vector3(point, 0f);
            Vector3 farSource = new Vector3(point, 1f);

            // use Viewport.Unproject to tell what those two screen space positions
            // would be in world space. we'll need the projection matrix and view
            // matrix, which we have saved as member variables. We also need a world
            // matrix, which can just be identity.
            Vector3 nearPoint = _GraphicsDevice.Viewport.Unproject(nearSource,
                camera.Projection, camera.View, Matrix.Identity);

            Vector3 farPoint = _GraphicsDevice.Viewport.Unproject(farSource,
                camera.Projection, camera.View, Matrix.Identity);

            // find the direction vector that goes from the nearPoint to the farPoint
            // and normalize it....
            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();

            // and then create a new ray using nearPoint as the source.
            return new Ray(nearPoint, direction);
        }

        private void RayIntersectsTriangle(ref Ray ray,
                                          ref Vector3 vertex1,
                                          ref Vector3 vertex2,
                                          ref Vector3 vertex3, out float? result)
        {
            // Compute vectors along two edges of the triangle.
            Vector3 edge1, edge2;

            Vector3.Subtract(ref vertex2, ref vertex1, out edge1);
            Vector3.Subtract(ref vertex3, ref vertex1, out edge2);

            // Compute the determinant.
            Vector3 directionCrossEdge2;
            Vector3.Cross(ref ray.Direction, ref edge2, out directionCrossEdge2);

            float determinant;
            Vector3.Dot(ref edge1, ref directionCrossEdge2, out determinant);

            // If the ray is parallel to the triangle plane, there is no collision.
            if (determinant > -float.Epsilon && determinant < float.Epsilon)
            {
                result = null;
                return;
            }

            float inverseDeterminant = 1.0f / determinant;

            // Calculate the U parameter of the intersection point.
            Vector3 distanceVector;
            Vector3.Subtract(ref ray.Position, ref vertex1, out distanceVector);

            float triangleU;
            Vector3.Dot(ref distanceVector, ref directionCrossEdge2, out triangleU);
            triangleU *= inverseDeterminant;

            // Make sure it is inside the triangle.
            if (triangleU < 0 || triangleU > 1)
            {
                result = null;
                return;
            }

            // Calculate the V parameter of the intersection point.
            Vector3 distanceCrossEdge1;
            Vector3.Cross(ref distanceVector, ref edge1, out distanceCrossEdge1);

            float triangleV;
            Vector3.Dot(ref ray.Direction, ref distanceCrossEdge1, out triangleV);
            triangleV *= inverseDeterminant;

            // Make sure it is inside the triangle.
            if (triangleV < 0 || triangleU + triangleV > 1)
            {
                result = null;
                return;
            }

            // Compute the distance along the ray to the triangle.
            float rayDistance;
            Vector3.Dot(ref edge2, ref distanceCrossEdge1, out rayDistance);
            rayDistance *= inverseDeterminant;

            // Is the triangle behind the ray origin?
            if (rayDistance < 0)
            {
                result = null;
                return;
            }

            result = rayDistance;
        }

        public bool CheckInside(Vector2 point)
        {
            Vector2 upperLeft = new Vector2(_PlanarModel.Vertices[1].Position.X, _PlanarModel.Vertices[1].Position.Y);
            Vector2 lowerRight = new Vector2(_PlanarModel.Vertices[2].Position.X, _PlanarModel.Vertices[2].Position.Y);
            if (point.X >= upperLeft.X && point.X <= lowerRight.X &&
                point.Y >= lowerRight.Y && point.Y <= upperLeft.Y)
                return true;
            return false;
        }
    }
}
